extends CharacterBody2D

enum State{
	IDLE,
	RUNNING,
	JUMP,
	FALL,
	LANDING,
}

const GROUND_STATE := [ State.IDLE, State.RUNNING, State.LANDING] # 在地板上的角色状态
const FLOOR_ACCELERATION = RUN_SPEED / 0.2
const AIR_CCELERATION = RUN_SPEED / 0.02
const RUN_SPEED := 160.0
const JUMP_VELOCITY := -320.0

var default_gravity := ProjectSettings.get("physics/2d/default_gravity") as float
var is_first_tick := false # 是否是第一帧

@onready var animation_player: AnimationPlayer = $AnimationPlayer
@onready var sprit_2d: Sprite2D = $Sprite2D
@onready var coyote_timer: Timer = $CoyoteTimer
@onready var jump_request_timer: Timer = $JumpRequestTimer

# 按键事件监听回调函数
func _unhandled_input(event: InputEvent) -> void:
	if event.is_action_pressed("jump"):
		# 可以试试，这样会有个特性，正式落地前起跳并且在落地前松开空格，那一跳必然是大跳，因为没有「松开」的行为了，其实这样也很怪
		jump_request_timer.start()
	# 跳跃键短按
	if event.is_action_released("jump"):
		jump_request_timer.stop() 
		if velocity.y < JUMP_VELOCITY / 2:
			# 松开跳跃键时当前高度小于角色能跳跃到的极限的一半，就将此次跳跃高度设为正常跳跃高度的一半
			velocity.y = JUMP_VELOCITY / 2 

func tick_physics(state: State, delta: float) -> void:
	match state:
		State.IDLE: #站立
			move(default_gravity,delta)
		State.RUNNING:
			move(default_gravity,delta)
		State.JUMP:
			move(0.0 if is_first_tick else default_gravity, delta)
		State.FALL:
			move(default_gravity,delta)
		State.LANDING:
			stand(delta)
			
	is_first_tick = false
	

func move(gravity: float, delta: float) -> void:
	var direction := Input.get_axis("move_left","move_right")
	var acceleration := FLOOR_ACCELERATION if is_on_floor() else AIR_CCELERATION
	velocity.x = move_toward(velocity.x, direction * RUN_SPEED,acceleration * delta)
	velocity.y += gravity * delta
	
	if not is_zero_approx(direction):
		sprit_2d.flip_h = direction < 0
		
	move_and_slide()
	
func stand(delta:float) -> void:
	var acceleration := FLOOR_ACCELERATION if is_on_floor() else AIR_CCELERATION
	velocity.x = move_toward(velocity.x, 0.0, acceleration * delta)
	velocity.y += default_gravity * delta

	move_and_slide()
	
func get_next_state(state: State) -> State:
	var can_jump := is_on_floor() or coyote_timer.time_left > 0
	var should_jump := can_jump and jump_request_timer.time_left > 0
	if should_jump:
		return State.JUMP
	var direction := Input.get_axis("move_left","move_right")
	var is_still := is_zero_approx(direction) and is_zero_approx(velocity.x)
	match state:
		State.IDLE: #站立
			if not is_on_floor():
				return State.FALL
			if not is_still:
				return State.RUNNING
			
		State.RUNNING:
			if not is_on_floor():
				return State.FALL
			if is_still:
				return State.IDLE
			
		State.JUMP:
			if velocity.y >= 0:
				return State.FALL
			
		State.FALL:
			if is_on_floor():
				# 着陆动作只有在静止状态时在播放，如果不是静止就要播放跑动动画
				return State.LANDING if is_still else State.RUNNING
		
		State.LANDING:
			if not animation_player.is_playing():
				return State.IDLE
			
	return state

func transition_state(from:State, to:State) -> void:
	if from not in GROUND_STATE and to in GROUND_STATE:
		coyote_timer.stop()
	
	match to:
		State.IDLE:
			animation_player.play("idle")
		
		State.RUNNING:
			animation_player.play("running")
			
		State.JUMP:
			animation_player.play("jump")
			velocity.y = JUMP_VELOCITY
			coyote_timer.stop()
			jump_request_timer.stop()
			
		State.FALL:
			animation_player.play("fall")
			if from in GROUND_STATE:
				coyote_timer.start()
				
		State.LANDING:
			animation_player.play("landing")		
		
	is_first_tick = true	
	
			
			
			
			
